/*

This program file is part of luacalc
(C) 2006 Richard Simes
You may distribute it under the terms of the GNU General Public License

*/
#include "luacalcui.h"
#include "luacalchelp.h"
#include "luacalcgrid.h"
#include <wx/filedlg.h>

#include <wx/clipbrd.h>

#include <iostream>
using namespace std;


LuaCalcFrame::LuaCalcFrame(wxWindow* parent, int id, const wxString& title, const wxPoint& pos, const wxSize& size, long style):
		wxFrame(parent, id, title, pos, size, wxDEFAULT_FRAME_STYLE)
{
	grid = new wxGrid(this, -1);
	macroRunning = false;
	
	// create menubar
	wxMenuBar *menuBar = new wxMenuBar;
	// create menu
	wxMenu *menuFile = new wxMenu;
	// append menu entries
	menuFile->Append(ID_Open,_("&Open	Ctrl-o"), _("Open a new file"));
	menuFile->Append(ID_Save,_("&Save	Ctrl-s"), _("Save this file"));
	menuFile->Append(ID_Sort,_("So&rt	Ctrl-t"), _("Sort all the data by the selected column"));
	menuFile->AppendSeparator();
	menuFile->Append(ID_Quit,_("E&xit	Ctrl-q"), _("Quit luacalc"));
	// append menu to menubar
	menuBar->Append(menuFile,_("&File"));
	
	
	wxMenu* edit = new wxMenu();
	edit->Append(ID_Undo,_("&Undo"), _("Ctrl-z"));
	edit->Append(ID_Redo,_("&Redo"), _("Ctrl-r"));
	edit->AppendSeparator();
	edit->Append(ID_SaveMacro, _("&Save Macro"));
	edit->Append(ID_LoadMacro, _("&Load Macro"));
	edit->Append(ID_Plot, _("&Plot"));
	
//	functions = new wxMenu;

//	edit->Append(ID_Functions, _("Insert &Function"), functions);
	
	
	menuBar->Append(edit,_("&Edit"));
	
	wxMenu* help = new wxMenu();
	
	help->Append(ID_About,_("&About"), _("About luacalc"));
	help->Append(ID_Help,_("Function &Reference"), _("List of functions"));
	
	
	menuBar->Append(help, _("&Help"));
	// set frame menubar
	SetMenuBar(menuBar);
	
	// create frame statusbar
	CreateStatusBar();
	// set statusbar text
	SetStatusText(_("Welcome to LuaCalc"));
	
	
	SetProperties();
	DoLayout();
// end wxGlade
}



void LuaCalcFrame::SetProperties()
{
	SetTitle(wxT("LuaCalc"));
	SetSize(wxSize(820, 307));
	SetFocus();
	grid->SetTable(new LuaCalcGrid(grid), true, wxGrid::wxGridSelectCells);
	L = ((LuaCalcGrid*)grid->GetTable())->L;
	grid->SetFocus();
	
}


void LuaCalcFrame::DoLayout()
{
  wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
  sizer->Add(grid, 1, wxEXPAND, 0);
  SetAutoLayout(true);
  SetSizer(sizer);
  Layout();
}

void LuaCalcFrame::Save(){
  //lua_State* L = ((LuaCalcGrid*)grid->GetTable())->L;
  
  if (filename==NULL){
    wxFileDialog* file = new wxFileDialog(this, _("Choose a file to save"),  _(""),  _(""), _(""),wxSAVE);
    
    if (file->ShowModal()==wxID_OK){
      int len = file->GetPath().Len();
      char* fname = new char[len+1];    
      filename = strncpy(fname, file->GetPath().mb_str(wxConvUTF8), len+1);      
    }
    delete file;
  }
  if (filename==NULL) return;
  // cout << filename << endl;
  lua_getfield(L, LUA_GLOBALSINDEX, "save");
  lua_pushstring(L, filename);
  lua_call(L, 1, 0);
  
}



/*
	Open a new file.
*/
void LuaCalcFrame::Open(){
	if  (IsDirty()){
		wxMessageDialog d(this, _("This file has been modified. Save before opening a new file?"), _("Save File?"), wxYES_NO);
		if (d.ShowModal() == wxID_YES){
			Save();
		}
	}
	
  	wxFileDialog* file = new wxFileDialog(this, _("Choose a file to open"),  _(""),  _(""));
	//lua_State* L = ((LuaCalcGrid*)grid->GetTable())->L;
	if (file->ShowModal()==wxID_OK){
		if (filename!=NULL){
			delete [] filename;
		}
		int len = file->GetPath().Len();
		char* fname = new char[len+1];
		
		filename = strncpy(fname, file->GetPath().mb_str(wxConvUTF8), len+1);
		//	cout << filename << " : " << len << endl;
		lua_getfield(L, LUA_GLOBALSINDEX, "open");
		lua_pushstring(L, filename);
		lua_call(L, 1, 0);
		SetTitle(file->GetPath().Append(wxT(" - LuaCalc")));
	}
	
	delete file;
}
void LuaCalcFrame::Sort(){
	if (grid->GetSelectedCols().Count()>0){
		int col = grid->GetSelectedCols()[0] + 1;
		//lua_State* L = ((LuaCalcGrid*)grid->GetTable())->L;
		lua_getfield(L, LUA_GLOBALSINDEX, "luacalc");
		lua_getfield(L, -1, "sort");
		lua_remove(L, -2);
		lua_pushinteger(L, col);
		lua_call(L, 1, 0);		
		grid->ForceRefresh();
	} else {
		  wxMessageBox(_("Select a column to sort the data by."),_("About: Sorting"),
	       wxOK|wxICON_INFORMATION, this);

	}
}
	

void LuaCalcFrame::Quit(bool close){
	if (close){
		if  (IsDirty()){
			wxMessageDialog d(this, _("This file has been modified. Save before quit?"), _("Save File?"), wxYES_NO);
			if (d.ShowModal() == wxID_YES){
				Save();
			}
		}
	
		Close(true);	
	} else {
		if  (IsDirty()){
			wxMessageDialog d(NULL, _("This file has been modified. Save before quit?"), _("Save File?"), wxYES_NO);
			if (d.ShowModal() == wxID_YES){
				Save();
			}
		}
	


	}
}


/* Event Handlers */


/**
 *	Quit Luacalc.
 *	Prompts to save the file if it has been modified.
 */
void LuaCalcFrame::OnQuit(wxCommandEvent& event){
 // cout << " on quit called " << endl;
	Quit();
}


void LuaCalcFrame::OnLoadMacro(wxCommandEvent& event){

  	wxFileDialog* file = new wxFileDialog(this, _("Choose a macro file to open"),  _(""),  _(""));
	//lua_State* L = ((LuaCalcGrid*)grid->GetTable())->L;
	if (file->ShowModal()==wxID_OK){
		
		lua_getglobal(L, "luacalc");
		lua_getfield(L, -1, "loadmacro");
		lua_remove(L, -2);
		lua_pushstring(L, file->GetPath().mb_str(wxConvUTF8));
		lua_call(L, 1, 0);
			
	}
	
	delete file;
}

void LuaCalcFrame::OnSaveMacro(wxCommandEvent& event){
	//lua_State* L = ((LuaCalcGrid*)grid->GetTable())->L;
	
	wxFileDialog* file = new wxFileDialog(this, _("Choose a file to save"),  _(""),  _(""), _(""),wxSAVE);
	
	if (file->ShowModal()==wxID_OK){
		
		lua_getglobal(L, "luacalc");
		lua_getfield(L, -1, "savemacro");
		lua_remove(L, -2);
		lua_pushstring(L, file->GetPath().mb_str(wxConvUTF8));
		lua_call(L, 1, 0);
	}

	delete file;
  
  
}

void LuaCalcFrame::OnAbout(wxCommandEvent& event){
  wxMessageBox(_("LuaCalc - A Simple Spreadsheet Program"),_("About: LuaCalc"),
	       wxOK|wxICON_INFORMATION, this);

}
void LuaCalcFrame::OnOpen(wxCommandEvent& event){
	Open();
}

void LuaCalcFrame::OnSave(wxCommandEvent& event){
	Save();  
}

void LuaCalcFrame::OnHelp(wxCommandEvent& event){
// can't seem to connect the select event properly...
/*
	HelpFrame* frame = new HelpFrame(this, 50, _(""));
	//SetTopWindow(frame);
	frame->GetFunctions(((LuaCalcGrid*)grid->GetTable())->L);
	frame->Show();
*/
	int err;
	if (IsDirty()) 
		Save();
	//lua_State* L = ((LuaCalcGrid*)grid->GetTable())->L;

	char fname[strlen(INSTALL_PREFIX)+strlen("lchelp.lua")+2];
	sprintf(fname, "%s%s", INSTALL_PREFIX, "/lchelp.lua");

	/* load the file. */
	if ((err=luaL_loadfile(L, fname))==0){
		// Push the function name onto the stack
		if ((err = lua_pcall (L, 0, LUA_MULTRET, 0)) == 0){
			lua_pushstring (L, "hello");
			// Function is located in the Global Table
			lua_gettable (L, LUA_GLOBALSINDEX);
			lua_pcall (L, 0, 0, 0);
		}else{
			cerr << "couldn't load main" << endl;
			exit(1);
		}
	} else{
		std::cerr << "couldn't load file" << fname << std::endl;
		exit(2);
	}
	grid->SetDefaultRowSize(100, true);
	grid->AutoSizeColumn(0);
	grid->AutoSizeColumn(1);
	grid->ForceRefresh();

}


void LuaCalcFrame::OnPlot(wxCommandEvent& event){
	//lua_State* L = ((LuaCalcGrid*)grid->GetTable())->L;
	lua_getglobal(L, "luacalc");
	lua_getfield(L, -1, "plot");
	lua_remove(L, -2);
	lua_call(L, 0, 0);

}
	


/**
   Puts the formula entered into the selected cell
   into the status bar.
*/
void LuaCalcFrame::OnSelect(wxGridEvent& event){
	
	//lua_State* L = ((LuaCalcGrid*)grid->GetTable())->L;
	
	col = event.GetCol();
	row = event.GetRow();
	
	//switch to 1 based indices
	col++;
	row++;
	
	lua_getfield(L, LUA_GLOBALSINDEX, "getformula");
	lua_pushinteger(L, row);
	lua_pushinteger(L, col);
	lua_call(L, 2, 1);
	
	size_t* len = new size_t;
	/* return value is at the top of the stack. */
	const char* str = lua_tolstring(L, lua_gettop(L), len);
	lua_pop(L, 1);
	if (*len > 0 and *len < 50){
		wxString st(str, wxConvUTF8);
		SetStatusText(st);
	}	
	// allow the selection to carry on as normal.
	
	event.Skip();
}


/**
 * called when the user presses the 'x' button.
 */

void LuaCalcFrame::OnExit(wxCloseEvent& event)
{

	Quit(false);	
	//delete frame;
	event.Skip();

}





/**
  Process key events.
  only skip the event for keys we don't use.
*/
void LuaCalcFrame::OnKeyDown(wxKeyEvent& event){

	if (event.CmdDown()){
		switch(event.GetKeyCode()){
			case 'C' : CopyData(); 	return;
			case 'O' : Open();	return;
			case 'T' : Sort();	return;
			case 'S' : Save();	return;	
			case 'V' : PasteData(); return;	
			case 'Z' : Undo(); 	return;
			case 'R' : Redo(); 	return;
			case 'Q' : Quit(); 	return;
			case 'L' : Record();	return;
			case 'M' : Replay();	return;
		}	
	} else if (macroRunning){
		//cout << event.GetKeyCode() << endl;
		switch(event.GetKeyCode()){
			case 318:	move(0, 1);	break; // >
			case 319:	move(1, 0);	break; // v
			case 316:	move(0, -1);	break; // <
			case 317:	move(-1, 0);	break; // ^
			case 13:	move(1, 0);	break; // Return
		}
	}
	event.Skip();
}

/**
 * Start or stop a macro recording.
 * NOT YET FUNCTIONAL.
 */
void LuaCalcFrame::Record()
{ 
	//lua_State* L = ((LuaCalcGrid*)grid->GetTable())->L;
	lua_getfield(L, LUA_GLOBALSINDEX, "luacalc");
	if (macroRunning){
		macroRunning = false;
		lua_getfield(L, -1, "stopmacro"); 
		lua_remove(L, -2);
		lua_call(L, 0, 0);
		
	} else {
		macroRunning = true;
		lua_getfield(L, -1, "startmacro"); 
		lua_remove(L, -2);
		lua_pushinteger(L, row);
		lua_pushinteger(L, col);
		lua_call(L, 2, 0);
	
	}
}



void LuaCalcFrame::Replay(){
	//lua_State* L = ((LuaCalcGrid*)grid->GetTable())->L;
	lua_getfield(L, LUA_GLOBALSINDEX, "luacalc");
	lua_getfield(L, -1, "replay"); 
	lua_remove(L, -2);
	lua_call(L, 0, 0);
	grid->ForceRefresh();
}


void LuaCalcFrame::move(int x, int y){
	//lua_State* L = ((LuaCalcGrid*)grid->GetTable())->L;
	lua_getfield(L, LUA_GLOBALSINDEX, "luacalc");
	lua_getfield(L, -1, "move"); 
	lua_remove(L, -2);
	lua_pushinteger(L, x);
	lua_pushinteger(L, y);
	lua_call(L, 2, 0);
}




/**
 * Primitive undo function.
 * Undoes the last call to set or paste.
 * Sort is not yet supported.
 */

void LuaCalcFrame::Undo(){
  //lua_State* L = ((LuaCalcGrid*)grid->GetTable())->L;
  lua_getfield(L, LUA_GLOBALSINDEX, "luacalc");      
  lua_getfield(L, -1, "undo");
  lua_remove(L, -2);
  lua_call(L, 0, 1);
	  
  size_t* len = new size_t;
  /* return value is at the top of the stack. */
  const char* str = lua_tolstring(L, lua_gettop(L), len);
  lua_pop(L, 1);
  if (*len > 0 and *len < 50){
    wxString st(str, wxConvUTF8);
    SetStatusText(st);
  }
  grid->ForceRefresh();
}

void LuaCalcFrame::Redo(){
  //lua_State* L = ((LuaCalcGrid*)grid->GetTable())->L;
  lua_getfield(L, LUA_GLOBALSINDEX, "luacalc");      
  lua_getfield(L, -1, "redo");                 
  lua_remove(L, -2);
  lua_call(L, 0, 1);
  size_t* len = new size_t;
  /* return value is at the top of the stack. */
  const char* str = lua_tolstring(L, lua_gettop(L), len);
  if (*len > 0 and *len < 50){
    wxString st(str, wxConvUTF8);
    SetStatusText(st);
  }
  grid->ForceRefresh();
}



/**
	Calls luacalc.sort with the selected col
*/
void LuaCalcFrame::OnSort(wxCommandEvent& WXUNUSED(event)){
  Sort();
	
}

void LuaCalcFrame::OnUndo(wxCommandEvent& event){
	Undo();
}
void LuaCalcFrame::OnRedo(wxCommandEvent& event)
{
	Redo();
}


/**
  Copy selected data to the clipboard.
  should be compatible with other spreadsheet programs.

*/
void LuaCalcFrame::CopyData(){
  int i,k;
  wxString copy_data;
  bool something_in_this_line;
  
  copy_data.Clear();
  
  for (i=0; i<grid->GetRows(); i++) {     // step through all lines
    something_in_this_line = false;     // nothing found yet
    for (k=0; k<grid->GetCols(); k++) { // step through all colums
      if (grid->IsInSelection(i,k)) { // this field is selected
	if (!something_in_this_line) {  // first field in this line => may need a linefeed
	  if (!copy_data.IsEmpty()) {     // ... if it is not the very first field
	    copy_data.Append(wxT("\n"));  // next LINE
	  }
	  something_in_this_line = true;
	} else {                                // if not the first field in this line we need a field seperator (TAB)
	  // copy_data = copy_data + wxT("\t");  // next COLUMN
	  copy_data.Append(wxT("\t"));  //should vbe faster?
	}
	copy_data.Append(grid->GetCellValue(i,k));    // finally we need the field value Smile
      }
    }
  }
  
  
  wxTheClipboard->Open();
  wxTheClipboard->Clear(); 
  //won't this cause a memory leak?
  // couldn't see a noticable problem using 'top'
  // perhaps the call to clear frees the memory?
  wxTheClipboard->SetData(new wxTextDataObject(copy_data));
  wxTheClipboard->Close();
  
  
}

/*
  Paste the data in the clipboard to the spreadsheet.
*/
void LuaCalcFrame::PasteData(){
  //lua_State* L = ((LuaCalcGrid*)grid->GetTable())->L;
  luaL_dostring(L, "luacalc.startpaste()");
  wxTextDataObject text;
  wxString copy_data;
  wxString cur_field;
  wxString cur_line;
  int i,k,k2;
  wxTheClipboard->Open();          
  
  wxTheClipboard->GetData(text);
  copy_data = text.GetText();
  wxTheClipboard->Close();          
  
    
  //insert starting at the cursor
  i = grid->GetGridCursorRow();
  k = grid->GetGridCursorCol();
  k2= k;
  
  do {
    cur_line = copy_data.BeforeFirst('\n');
    copy_data = copy_data.AfterFirst('\n');
    do {
      cur_field = cur_line.BeforeFirst('\t');
      cur_line  = cur_line.AfterFirst ('\t');
      grid->SetCellValue(i,k,cur_field);
      k++;
    } while(!cur_line.IsEmpty());
    i++;
    k = k2;
  } while (!copy_data.IsEmpty());
  
  luaL_dostring(L, "luacalc.endpaste()");
  
}

/*
	Returns true if the file has been modified since the last save.
*/
bool LuaCalcFrame::IsDirty()
{
	//lua_State* L = ((LuaCalcGrid*)grid->GetTable())->L;
	lua_getfield(L, LUA_GLOBALSINDEX, "luacalc");
	lua_getfield(L, -1, "dirty");
	return lua_toboolean(L, lua_gettop(L));
}




/*
  Main method
*/

IMPLEMENT_APP(LuaCalc)
  
bool LuaCalc::OnInit()
{
  wxInitAllImageHandlers();
/*  LuaCalcFrame**/frame = new LuaCalcFrame(0, -1, _(""));
  SetTopWindow(frame);
  frame->Show();
  
  return true;
}



